package com.xiaoshuidi.cloud.module.contract.service.contract.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.convert.NumberChineseFormatter;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.IdcardUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fadada.sdk.base.model.req.*;
import com.itextpdf.text.pdf.PdfReader;
import com.xiaoshuidi.cloud.framework.common.exception.ErrorCode;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.fadada.core.FadadaContractServiceApi;
import com.xiaoshuidi.cloud.framework.fadada.core.enums.FadadaReturnCodeConstants;
import com.xiaoshuidi.cloud.framework.fadada.core.service.dto.FadadaUploadResponse;
import com.xiaoshuidi.cloud.framework.filings.core.enums.PropertyTypeEnum;
import com.xiaoshuidi.cloud.framework.tenant.core.util.TenantUtils;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.ContractUpdateStatusVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.ElectronicContractGenerateRespVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.TabletContractRespVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.ownerContract.ContractHouseOwnerRespVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.signingSubject.ContractSignCallbackReqVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.signingSubject.ContractTabletSignCallbackReqVO;
import com.xiaoshuidi.cloud.module.contract.convert.contract.ContractHouseOwnerConvert;
import com.xiaoshuidi.cloud.module.contract.convert.contract.ContractRoomConvert;
import com.xiaoshuidi.cloud.module.contract.convert.contract.ElectronicContractConvert;
import com.xiaoshuidi.cloud.module.contract.convert.contract.TemplateDynamicTablesConvert;
import com.xiaoshuidi.cloud.module.contract.convert.room.RoomServerConvert;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.bill.RentOrderDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.*;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.roommeta.ContractRoommateDO;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.ContractHouseOwnerPropertyMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.ElectronicContractMapper;
import com.xiaoshuidi.cloud.module.contract.enums.ApiConstants;
import com.xiaoshuidi.cloud.module.contract.enums.bill.OrderTypeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.contract.*;
import com.xiaoshuidi.cloud.module.contract.service.bill.OwnerOrderService;
import com.xiaoshuidi.cloud.module.contract.service.bill.dto.RentOrderQueryDTO;
import com.xiaoshuidi.cloud.module.contract.service.contract.*;
import com.xiaoshuidi.cloud.module.contract.service.contract.dto.*;
import com.xiaoshuidi.cloud.module.contract.service.roommate.ContractRoommateService;
import com.xiaoshuidi.cloud.module.contract.util.RedisUtils;
import com.xiaoshuidi.cloud.module.contract.util.bill.BillPeriod;
import com.xiaoshuidi.cloud.module.contract.util.date.DateUtils;
import com.xiaoshuidi.cloud.module.contract.util.pdf.MatchItem;
import com.xiaoshuidi.cloud.module.contract.util.pdf.PdfKeyWordUtil;
import com.xiaoshuidi.cloud.module.contract.util.pdf.PdfUtils;
import com.xiaoshuidi.cloud.module.member.api.user.MemberUserApi;
import com.xiaoshuidi.cloud.module.member.api.user.dto.MemberUserAuthInfoDTO;
import com.xiaoshuidi.cloud.module.rooms.api.RoomEquipmentApi;
import com.xiaoshuidi.cloud.module.rooms.api.roomhouse.RoomHouseApi;
import com.xiaoshuidi.cloud.module.rooms.api.roomhouse.vo.RoomHouseFilingsResp;
import com.xiaoshuidi.cloud.module.rooms.vo.RoomEquipmentVO;
import com.xiaoshuidi.cloud.module.rooms.vo.RoomHouseRespVo;
import com.xiaoshuidi.cloud.module.system.api.dict.DictDataApi;
import com.xiaoshuidi.cloud.module.system.api.dict.dto.DictDataRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.util.*;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.module.contract.enums.ApiConstants.*;
import static com.xiaoshuidi.cloud.module.contract.enums.ErrorCodeConstants.*;
import static com.xiaoshuidi.cloud.module.contract.util.bill.BillPeriod.splitByMonth;

/**
 * 电子合同 Service 实现类
 *
 * @author 合房租赁
 */
@Service
@Slf4j
@RefreshScope
public class ElectronicContractServiceImpl implements ElectronicContractService {
    @Value("${fadada.extsign_callback_url}")
    private String EXTSIGN_CALLBACK_URL;
    @Value("${fadada.extsign_sync_callback_url}")
    private String EXTSIGN_SYNC_CALLBACK_URL;
    @Value("${fadada.security_protocol_file}")
    private String SECURITY_PROTOCOL_FILE;
    @Value("${fadada.leaser_code}")
    private String LEASER_CODE;
    @Value("${fadada.security_protocol_callback_url}")
    private String SECURITY_PROTOCOL_CALLBACK_URL;
    @Value("${fadada.security_protocol_sync_callback_url}")
    private String SECURITY_PROTOCOL_SYNC_CALLBACK_URL;
    @Value("${fadada.owner_extsign_sync_callback_url}")
    private String OWNER_EXTSIGN_SYNC_CALLBACK_URL;

    @Value("${fadada.sign_target_url:}")
    private String SIGN_TARGET_URL;

    @Value("${fadada.sign_replace_url:}")
    private String SIGN_REPLACE_URL;
    @Resource
    private ElectronicContractMapper electronicContractMapper;
    @Resource
    private ContractRoomService contractRoomService;
    @Resource
    private SigningSubjectService signingSubjectService;
    @Resource
    private ContractTemplateService contractTemplateService;
    @Resource
    private RoomHouseApi roomHouseApi;
    @Resource
    private FadadaContractServiceApi fadadaContractServiceApi;
    @Resource
    private RoomEquipmentApi roomEquipmentApi;
    @Resource
    private CustomerCompanyService customerCompanyService;
    //    @Resource
//    private IotRoomDeviceApi iotRoomDeviceApi;
    @Resource
    private MemberUserApi memberUserApi;
    @Resource
    private ContractRoommateService contractRoommateService;
    @Resource
    private DictDataApi dictDataApi;
    @Resource
    private ContractHouseOwnerService contractHouseOwnerService;
    @Resource
    private OwnerOrderService ownerOrderService;
    @Resource
    private ContractHouseOwnerPropertyMapper ownerPropertyMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ElectronicContractGenerateRespVO generateElectronicContract(Long contractId) {
        ContractRoomDO contractDO = getRoomContract(contractId);
        if (Objects.isNull(contractDO.getApartmentId())) {
            throw exception(CONTRACT_DATA_ABNORMAL);
        }

        ElectronicContractGenerateRespVO respVO;
        //查询电子合同信息
        ElectronicContractDO electronicContractDO = getElectronicContract(contractId);
        if (Objects.nonNull(electronicContractDO) && StringUtils.isNotBlank(electronicContractDO.getDownloadUrl())
                && StringUtils.isNotBlank(electronicContractDO.getViewpdfUrl())) {
            // 有预览地址下载地址数据  直接返回
            respVO = ElectronicContractConvert.INSTANCE.convert(electronicContractDO);
            return respVO;
        }

        // 查询主体实名信息
        SigningSubjectDO subjectDO = getSubjectDTO(contractDO.getApartmentId());
        // 查询房源房间信息
        RoomHouseRespDTO roomInfoFromRoom = getRoomInfoFromRoom(contractDO.getRoomId());
        //查询合同模板
        ContractTemplateDO contractTemplate = getContractTemplate(contractDO.getApartmentId(), roomInfoFromRoom.getIsWhole(), ContractTemplateTypeEnum.RENT_CONTRACT.getCode());

        //生成电子合同编号
        String contractNum = generateNumber(EL_CONTRACT_PREFIX + contractId + UNDERLINE, EL_CONTRACT_REDIS_PREFIX);

        contractDO.setElectronicNo(contractNum);

        // 需要填充的模板数据
        String parameterMap = getTemplateParamerMap(contractDO, contractTemplate);
        // 需要填充的动态表格数据
        String dynamicTables = getTemplateDynamicTables(contractTemplate, contractDO.getRoomId());

        // 法大大填充数据返回
        FadadaUploadResponse fadadaUploadResponse = sendFillContract(contractTemplate.getTemplateNo(), contractNum, parameterMap, dynamicTables);
        if (Objects.nonNull(fadadaUploadResponse) && Objects.nonNull(fadadaUploadResponse.getCode())
                && StringUtils.equals(fadadaUploadResponse.getCode(), FadadaReturnCodeConstants.OPERATION_SUCCESS.getCode().toString())) {
            if (Objects.isNull(electronicContractDO)) {
                //新增电子合同
                electronicContractDO = new ElectronicContractDO();
                electronicContractDO.setRoomContractId(contractDO.getId());
                electronicContractDO.setLandlordSignature(subjectDO.getSignatureCode());
                electronicContractDO.setStatus(ZERO);
                electronicContractDO.setProvide(ONE);
                String viewUrl = fadadaUploadResponse.getViewpdf_url();
                if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
                    viewUrl = viewUrl.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
                }
                electronicContractDO.setViewpdfUrl(viewUrl);
                String downloadUrl = fadadaUploadResponse.getDownload_url();
                if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
                    downloadUrl = downloadUrl.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
                }
                electronicContractDO.setDownloadUrl(downloadUrl);
                electronicContractDO.setContractNum(contractNum);
                electronicContractDO.setLandlordTransactionId(contractNum);
                electronicContractDO.setType(ContractTemplateTypeEnum.RENT_CONTRACT.getCode());
                electronicContractDO.setSignType(contractDO.getSignType());
                electronicContractMapper.insert(electronicContractDO);
            } else {
                // 编辑电子合同
                electronicContractDO.setRoomContractId(contractDO.getId());
                electronicContractDO.setLandlordSignature(subjectDO.getSignatureCode());
                electronicContractDO.setStatus(ZERO);
                electronicContractDO.setProvide(ONE);
                String viewUrl = fadadaUploadResponse.getViewpdf_url();
                if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
                    viewUrl = viewUrl.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
                }
                electronicContractDO.setViewpdfUrl(viewUrl);
                String downloadUrl = fadadaUploadResponse.getDownload_url();
                if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
                    downloadUrl = downloadUrl.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
                }
                electronicContractDO.setDownloadUrl(downloadUrl);
                electronicContractDO.setContractNum(contractNum);
                electronicContractDO.setLandlordTransactionId(contractNum);
                electronicContractDO.setType(ContractTemplateTypeEnum.RENT_CONTRACT.getCode());
                electronicContractDO.setSignType(contractDO.getSignType());
                electronicContractMapper.updateById(electronicContractDO);
            }
            // 更新合同信息
            ContractUpdateStatusVO contractReq = new ContractUpdateStatusVO();
            contractReq.setId(contractDO.getId());
            contractReq.setElectronicNo(contractNum);
            contractRoomService.updateStatusAndElectronicNo(contractReq);

            respVO = ElectronicContractConvert.INSTANCE.convert(electronicContractDO);
            return respVO;
        } else {
            throw exception(CONTRACT_TEMPLATE_FILLING_FAILED, fadadaUploadResponse.getMsg());
        }

    }

    @NotNull
    private static String generateNumber(String numberPrefix, String redisPrefix) {
        String timeKey = LocalDate.now().format(DateTimeFormatter.ofPattern(DateUtils.FORMAT_NO_DATE));
        String code = RedisUtils.queryMaxCode(redisPrefix, timeKey);
        return numberPrefix + timeKey + code;
    }

    private FadadaUploadResponse sendFillContract(String templateNo, String contractNum, String parameterMap, String dynamicTables) {
        log.info("合同模板填充内容：{}", parameterMap);
        log.info("合同模板动态表格：{}", dynamicTables);
        //调用法大大填充合同模板
        GenerateContractParams params = new GenerateContractParams();
        //模板编号
        params.setTemplateId(templateNo);
        //合同编号
        params.setContractId(contractNum);
        //字体大小
        params.setFontSize(CONTRACT_FILLING_FONT_SIZE);
        //字体类型
        params.setFontType(CONTRACT_FILLING_FONT_TYPE);
        //填充内容,json字符串
        params.setParameterMap(parameterMap);
        //动态表格
        params.setDynamicTables(dynamicTables);
        FadadaUploadResponse fadadaUploadResponse = fadadaContractServiceApi.generateContract(params);
        log.info("法大大填充合同模板，请求参数：{}，返回参数：{}", JSONObject.toJSONString(params), JSONObject.toJSONString(fadadaUploadResponse));
        return fadadaUploadResponse;
    }

    private ContractRoomDO getRoomContract(Long contractId) {
        ContractRoomDO contractDO = contractRoomService.get(contractId);
        if (Objects.isNull(contractDO)) {
            throw exception(CONTRACT_DOES_NOT_EXISTS);
        }
        return contractDO;
    }

    private ElectronicContractDO getElectronicContract(Long contractId) {
        //查询电子合同记录
        LambdaQueryWrapper<ElectronicContractDO> electronicContractQueryWrapper = new LambdaQueryWrapper<>();
        electronicContractQueryWrapper.eq(ElectronicContractDO::getRoomContractId, contractId);
        electronicContractQueryWrapper.orderByDesc(ElectronicContractDO::getId);
        List<ElectronicContractDO> electronicContractDOS = electronicContractMapper.selectList(electronicContractQueryWrapper);
        if (CollectionUtils.isNotEmpty(electronicContractDOS)) {
            return electronicContractDOS.get(0);
        }
        return null;
    }


    private SigningSubjectDO getSubjectDTO(Long apartmentId) {
        SigningSubjectDO signingSubjectDO = signingSubjectService.getSubjectByApartmentId(apartmentId);
        if (Objects.isNull(signingSubjectDO)) {
            throw exception(CONTRACT_SUBJECT_NOT_EXIST);
        }
        if (Objects.isNull(signingSubjectDO.getStatus()) ||
                signingSubjectDO.getStatus().intValue() != SigningSubjectStatusEnum.EFFECTIVE.getCode().intValue()) {
            //主体未认证
            throw exception(CONTRACT_SUBJECT_NOT_AUTHENTICATION);
        }
        if (!signingSubjectDO.getSignSwitch()) {
            // 主体未开启线上签约
            throw exception(SUBJECT_NOT_OPEN_ONLINE_SIGN);
        }
        return signingSubjectDO;
    }

    private ContractTemplateDO getContractTemplate(Long apartmentId, Boolean isWhole, String templateType) {
        ContractTemplateDO contractTemplateDO = contractTemplateService.getTemplateByRoomId(apartmentId, Objects.nonNull(isWhole) ? (isWhole ? ZERO : ONE) : null, templateType);
        if (Objects.isNull(contractTemplateDO)) {
            //合同模板不存在
            throw exception(CONTRACT_TEMPLATE_NOT_EXISTS);
        }
        return contractTemplateDO;
    }

    /**
     * 查询房源服务房间信息
     */
    private RoomHouseRespDTO getRoomInfoFromRoom(Long roomId) {
        CommonResult<RoomHouseRespVo> roomHouseinfoById = roomHouseApi.getRoomHouseinfoById(roomId);
        if (Objects.isNull(roomHouseinfoById)) {
            throw exception(ROOM_NOT_EXIST);
        }
        RoomHouseRespVo data = roomHouseinfoById.getData();
        if (Objects.isNull(data)) {
            throw exception(ROOM_NOT_EXIST);
        }
        return RoomServerConvert.INSTANCE.converRespVo2RespDto(data);
    }

    private RoomInfoFillDTO getRoomInfo(Long roomId) {
        CommonResult<RoomHouseRespVo> roomHouseinfoById = roomHouseApi.getRoomHouseinfoById(roomId);
        if (Objects.isNull(roomHouseinfoById)) {
            throw exception(ROOM_NOT_EXIST);
        }
        RoomHouseRespVo data = roomHouseinfoById.getData();
        if (Objects.isNull(data)) {
            throw exception(ROOM_NOT_EXIST);
        }
        RoomInfoFillDTO roomInfoFillDTO = ElectronicContractConvert.INSTANCE.convertRoomHouseRespVo2RoomInfoFillDTO(data);
        // 地下楼层
        if (Objects.nonNull(roomInfoFillDTO.getTotalFloorNum()) && Objects.nonNull(roomInfoFillDTO.getGroundFloorNum())) {
            roomInfoFillDTO.setUndergroundFloorNum(roomInfoFillDTO.getTotalFloorNum() - roomInfoFillDTO.getGroundFloorNum());
        } else {
            roomInfoFillDTO.setUndergroundFloorNum(ZERO);
        }

//        // 水电表读数余额
//        CommonResult<RoomDeviceYe> deviceByRoomId = iotRoomDeviceApi.getDeviceByRoomId(roomId);
//        log.info("合同模板填充，查询水电表读数余额，参数：{}，返回参数：{}", roomId, deviceByRoomId);
//        if (Objects.nonNull(deviceByRoomId) && Objects.nonNull(deviceByRoomId.getData())) {
//            RoomDeviceYe roomDeviceYe = deviceByRoomId.getData();
//            roomInfoFillDTO.setCheckInHotWaterMeterReading(Objects.nonNull(roomDeviceYe.getHotWorterNo()) ? roomDeviceYe.getHotWorterNo().toString() : EMPTY);
//            roomInfoFillDTO.setCheckInHotWaterMeterBalance(Objects.nonNull(roomDeviceYe.getHotWorterBalance()) ? roomDeviceYe.getHotWorterBalance().toString() : EMPTY);
//            roomInfoFillDTO.setCheckInColdWaterMeterReading(Objects.nonNull(roomDeviceYe.getColdWorterNo()) ? roomDeviceYe.getColdWorterNo().toString() : EMPTY);
//            roomInfoFillDTO.setCheckInColdWaterMeterBalance(Objects.nonNull(roomDeviceYe.getColdWorterBalance()) ? roomDeviceYe.getColdWorterBalance().toString() : EMPTY);
//            roomInfoFillDTO.setCheckInElectricityMeterReading(Objects.nonNull(roomDeviceYe.getAmmterNo()) ? roomDeviceYe.getAmmterNo().toString() : EMPTY);
//            roomInfoFillDTO.setCheckInElectricityMeterBalance(Objects.nonNull(roomDeviceYe.getAmmterBalance()) ? roomDeviceYe.getAmmterBalance().toString() : EMPTY);
//        }
        return roomInfoFillDTO;
    }

    private ContractInfoFillDTO getContractInfoFillDTO(ContractRoomDO contractDO) {
        ContractInfoFillDTO contractInfoFill = ContractRoomConvert.INSTANCE.convertContractRoomDO2ContractInfoFillDTO(contractDO);
        //企业合同处理
        if (Objects.nonNull(contractDO.getCustomerType()) && StringUtils.equals(contractDO.getCustomerType(), CustomerTypeEnum.COMPANY.getCode())) {
            //查询企业信息
            CustomerCompanyDO companyDO = customerCompanyService.getCompanyDoById(contractDO.getCustomerCompanyId());
            //企业合同填充的数据
            contractInfoFill.setCustomerName(companyDO.getCompanyName());
            contractInfoFill.setCustomerPhone(companyDO.getContactsPhone());
        }
        // 承租方证件类型
        contractInfoFill.setCustomerIdTypeStr(IdTypeEnum.getName(contractInfoFill.getCustomerIdType()));
        //租期
        contractInfoFill.setStartYear(String.valueOf(contractDO.getStartTime().getYear()));
        contractInfoFill.setStartMonth(String.valueOf(contractDO.getStartTime().getMonthValue()));
        contractInfoFill.setStartDay(String.valueOf(contractDO.getStartTime().getDayOfMonth()));
        contractInfoFill.setEndYear(String.valueOf(contractDO.getEndTime().getYear()));
        contractInfoFill.setEndMonth(String.valueOf(contractDO.getEndTime().getMonthValue()));
        contractInfoFill.setEndDay(String.valueOf(contractDO.getEndTime().getDayOfMonth()));
        contractInfoFill.setSignedTimeYear(String.valueOf(contractDO.getSignedTime().getYear()));
        contractInfoFill.setSignedTimeMonth(String.valueOf(contractDO.getSignedTime().getMonthValue()));
        contractInfoFill.setSignedTimeDay(String.valueOf(contractDO.getSignedTime().getDayOfMonth()));
        //金额大写
        String monthRentCapital = Objects.nonNull(contractInfoFill.getMonthRent()) ? NumberChineseFormatter.format(contractInfoFill.getMonthRent().doubleValue(), true, true) : EMPTY;
        contractInfoFill.setMonthRentCapital(monthRentCapital);
        String totalRentCapital = Objects.nonNull(contractInfoFill.getTotalRent()) ? NumberChineseFormatter.format(contractInfoFill.getTotalRent().doubleValue(), true, true) : EMPTY;
        contractInfoFill.setTotalRentCapital(totalRentCapital);
        String depositCapital = Objects.nonNull(contractInfoFill.getDeposit()) ? NumberChineseFormatter.format(contractInfoFill.getDeposit().doubleValue(), true, true) : EMPTY;
        contractInfoFill.setDepositCapital(depositCapital);
        //租金支付周期
        contractInfoFill.setPayMethodStr(ContractPaymenEnum.getFillName(contractInfoFill.getPayMethodF()));

        String gender = getGenderByCardNo(contractInfoFill);
        if (StringUtils.isNotBlank(gender)) {
            contractInfoFill.setRenterSex(gender);
        }
        // 紧急联系人关系
        if (StringUtils.isNotBlank(contractInfoFill.getEmergencyContactRelationship())) {
            // 紧急联系人关系字典
            CommonResult<List<DictDataRespDTO>> relationshipDict = dictDataApi.getDictDataByType("emergency_contact");
            relationshipDict.checkError();
            List<DictDataRespDTO> dictList = relationshipDict.getData();
            if (CollectionUtils.isNotEmpty(dictList)) {
                DictDataRespDTO dictDataRespDTO = dictList.stream().filter(d -> d.getValue().equals(contractInfoFill.getEmergencyContactRelationship())).findFirst().orElse(null);
                if (Objects.nonNull(dictDataRespDTO)) {
                    contractInfoFill.setEmergencyContactRelationship(dictDataRespDTO.getLabel());
                }
            }
        }
        if (Objects.nonNull(contractInfoFill.getProperty())) {
            // 租金 + 物业费
            contractInfoFill.setRentPropertyAmount(contractInfoFill.getMonthRent().add(contractInfoFill.getProperty()));
            String rentPropertyAmountCapital = Objects.nonNull(contractInfoFill.getRentPropertyAmount()) ? NumberChineseFormatter.format(contractInfoFill.getRentPropertyAmount().doubleValue(), true, true) : EMPTY;
            contractInfoFill.setRentPropertyAmountCapital(rentPropertyAmountCapital);
            // 总物业费
            BigDecimal totalProperty = getTotalAmount(contractInfoFill.getStartTime(), contractInfoFill.getEndTime(), contractInfoFill.getPayMethodF(), contractInfoFill.getProperty());
            contractInfoFill.setTotalProperty(totalProperty);
            // 总租金 + 总物业费
            contractInfoFill.setTotalRentPropertyAmount(contractInfoFill.getTotalRent().add(totalProperty));
            String totalRentPropertyAmountCapital = Objects.nonNull(contractInfoFill.getTotalRentPropertyAmount()) ? NumberChineseFormatter.format(contractInfoFill.getTotalRentPropertyAmount().doubleValue(), true, true) : EMPTY;
            contractInfoFill.setTotalRentPropertyAmountCapital(totalRentPropertyAmountCapital);
            // 押金 + 总物业费
            contractInfoFill.setDepositPropertyAmount(contractInfoFill.getDeposit().add(totalProperty));
            String depositPropertyAmountCapital = Objects.nonNull(contractInfoFill.getDepositPropertyAmount()) ? NumberChineseFormatter.format(contractInfoFill.getDepositPropertyAmount().doubleValue(), true, true) : EMPTY;
            contractInfoFill.setDepositPropertyAmountCapital(depositPropertyAmountCapital);
        } else {
            contractInfoFill.setRentPropertyAmount(contractInfoFill.getMonthRent());
            contractInfoFill.setRentPropertyAmountCapital(contractInfoFill.getMonthRentCapital());
            contractInfoFill.setTotalRentPropertyAmount(contractInfoFill.getTotalRent());
            contractInfoFill.setTotalRentPropertyAmountCapital(contractInfoFill.getTotalRentCapital());
            contractInfoFill.setDepositPropertyAmount(contractInfoFill.getDeposit());
            contractInfoFill.setDepositPropertyAmountCapital(contractInfoFill.getDepositCapital());
        }

        // 延期日租金 月租金/30 * 3  不定期租赁期间日租金
        BigDecimal delayedDayRent = contractInfoFill.getMonthRent().divide(new BigDecimal("10"));
        contractInfoFill.setDelayedDayRent(delayedDayRent.setScale(2, RoundingMode.HALF_UP));

        // 租赁期(月份向上取整)
        if (Objects.nonNull(contractInfoFill.getStartTime()) && Objects.nonNull(contractInfoFill.getEndTime())){
            Period between = Period.between(contractInfoFill.getStartTime(), contractInfoFill.getEndTime());
            contractInfoFill.setLeaseTermMonth((between.getYears() * 12) + between.getMonths() + (between.getDays() + 1 > 0 ? 1 : 0));
        }

        return contractInfoFill;
    }

    private BigDecimal getTotalAmount(LocalDate startTime, LocalDate endTime, int payMethod, BigDecimal monthProperty) {
        BigDecimal totalProperty = BigDecimal.ZERO;
        //合同周期
        List<BillPeriod> billPeriods = splitByMonth(startTime, endTime, payMethod);
        for (BillPeriod billPeriod : billPeriods) {
            totalProperty = totalProperty.add(billPeriod.totalFee(monthProperty));
        }
        totalProperty = totalProperty.setScale(2, RoundingMode.HALF_UP);
        return totalProperty;
    }


    /**
     * 根据身份证号获取性别
     */
    private String getGenderByCardNo(ContractInfoFillDTO contractInfoFill) {
        //租客证件类型为身份证
        if (StringUtils.isNotBlank(contractInfoFill.getCustomerIdType()) &&
                contractInfoFill.getCustomerIdType().equals(IdTypeEnum.ID.getCode()) &&
                StringUtils.isNotBlank(contractInfoFill.getCustomerIdNumber())) {
            try {
                int genderByIdCard = IdcardUtil.getGenderByIdCard(contractInfoFill.getCustomerIdNumber());
                if (genderByIdCard == ZERO) {
                    return GENDER_WOMAN;
                } else if (genderByIdCard == ONE) {
                    return GENDER_MAN;
                }
            } catch (Exception e) {
                log.error("填充合同模板，租客身份证号有误");
            }
        }
        return null;
    }

    /**
     * 合同模板填充数据构造
     */
    private String getTemplateParamerMap(ContractRoomDO contractDO, ContractTemplateDO contractTemplate) {
        // 获取房源相关对象
        RoomInfoFillDTO roomInfoFillDTO = getRoomInfo(contractDO.getRoomId());
        log.info("合同模板填充，房源相关对象：{}", roomInfoFillDTO);
        // 获取合同相关对象
        ContractInfoFillDTO contractInfoFillDTO = getContractInfoFillDTO(contractDO);
        log.info("合同模板填充，合同相关对象：{}", contractInfoFillDTO);

        // 解析模板文件
        String[] templateFieldName = PdfUtils.getFieldName(contractTemplate.getTemplateUrl());
        if (null == templateFieldName || templateFieldName.length == ZERO) {
            return EMPTY;
        }
        // 模板文件中的关键字数据
        List<String> templateKeywords = new ArrayList<>(Arrays.asList(templateFieldName));

        // 模板关键字数据
        List<TemplateKeywardsDO> templateKeywardsList = contractTemplateService.getTemplateKeyWordsByTemplateId(contractTemplate.getId(), contractTemplate.getTemplateType());
        if (CollectionUtils.isEmpty(templateKeywardsList)) {
            return EMPTY;
        }
        //关键字
        JSONObject paramter = new JSONObject();
        //合同相关关键字映射
        Map<String, String> contractMap = new HashMap<>();
        //房源相关关键字映射
        Map<String, String> roomMap = new HashMap<>();
        // 默认值
        Map<String, String> defaultValueMap = new HashMap<>();
        for (TemplateKeywardsDO templateKeywardsDO : templateKeywardsList) {
            String mappingEntity = templateKeywardsDO.getMappingEntity();
            if (StringUtils.isNotBlank(templateKeywardsDO.getDefaultValue()) && templateKeywords.contains(templateKeywardsDO.getKeywords())) {
                // 关键字配置了默认值 且模板文件中包含此关键字
                defaultValueMap.put(templateKeywardsDO.getKeywords(), templateKeywardsDO.getDefaultValue());
            }
            if (StringUtils.isNotBlank(mappingEntity)) {
                String[] split = mappingEntity.split(SPOT);
                if (split.length == TWO) {
                    String entityType = split[ZERO];
                    if (templateKeywords.contains(templateKeywardsDO.getKeywords())) {
                        if (StringUtils.equals(entityType, CONTRACT_FILLING_ENTITY_CONTRACT)) {
                            contractMap.put(split[ONE], templateKeywardsDO.getKeywords());
                        } else if (StringUtils.equals(entityType, CONTRACT_FILLING_ENTITY_ROOM)) {
                            roomMap.put(split[ONE], templateKeywardsDO.getKeywords());
                        }
                    }
                }
            }
        }

        try {
            // 遍历出实体类中全部字段
            Field[] contractFields = ContractInfoFillDTO.class.getDeclaredFields();
            for (Field declaredField : contractFields) {
                // 取出实体类中的属性名称
                String fieldName = declaredField.getName();
                if (contractMap.containsKey(fieldName)) {
                    // 取出实体类中属性信息
                    Field declared = ContractInfoFillDTO.class.getDeclaredField(fieldName);
                    //抑制java语言访问检查，反射访问private访问权限的属性值
                    declared.setAccessible(true);
                    // 获取实体类中属性的值
                    Object value = declared.get(contractInfoFillDTO);
                    String keywordName = contractMap.get(fieldName);
                    //赋值
//                    paramter.put(keywordName, Objects.nonNull(value) ? value.toString() : EMPTY);
                    if (Objects.nonNull(value)) {
                        paramter.put(keywordName, value.toString());
                    } else if (!defaultValueMap.isEmpty() && defaultValueMap.containsKey(keywordName)) {
                        paramter.put(keywordName, defaultValueMap.get(keywordName));
                    } else {
                        paramter.put(keywordName, EMPTY);
                    }
                    if (!defaultValueMap.isEmpty() && defaultValueMap.containsKey(keywordName)) {
                        defaultValueMap.remove(keywordName);
                    }
                }
            }

            Field[] roomFields = RoomInfoFillDTO.class.getDeclaredFields();
            for (Field declaredField : roomFields) {
                String fieldName = declaredField.getName();
                if (roomMap.containsKey(fieldName)) {
                    Field declared = RoomInfoFillDTO.class.getDeclaredField(fieldName);
                    declared.setAccessible(true);
                    Object value = declared.get(roomInfoFillDTO);
                    String keywordName = roomMap.get(fieldName);
                    //赋值
//                    paramter.put(keywordName, Objects.nonNull(value) ? value.toString() : EMPTY);
                    if (Objects.nonNull(value)) {
                        paramter.put(keywordName, value.toString());
                    } else if (!defaultValueMap.isEmpty() && defaultValueMap.containsKey(keywordName)) {
                        paramter.put(keywordName, defaultValueMap.get(keywordName));
                    } else {
                        paramter.put(keywordName, EMPTY);
                    }
                    if (!defaultValueMap.isEmpty() && defaultValueMap.containsKey(keywordName)) {
                        defaultValueMap.remove(keywordName);
                    }
                }
            }
        } catch (Exception e) {
            log.error("合同模板填充，反射填充数据异常：", e);
            throw exception(TEMPLATE_KEYWARDS_ASSIGNMENT_ERROR);
        }

        if (!defaultValueMap.isEmpty()) {
            for (String key : defaultValueMap.keySet()) {
                paramter.put(key, defaultValueMap.get(key));
            }
        }

        log.info("合同模板填充，映射完成的关键字：{}", paramter);
        return paramter.toJSONString();
    }

    /**
     * 合同模板动态表单参数构建
     */
    private String getTemplateDynamicTables(ContractTemplateDO contractTemplate, Long roomId) {
        //查询模板动态表格数据
        List<TemplateDynamicTablesDO> templateDynamicTablesList = contractTemplateService.getTemplateDynamicTablesByTemplateId(contractTemplate.getId(), contractTemplate.getTemplateType());
        if (CollectionUtils.isEmpty(templateDynamicTablesList)) {
            return "";
        }
        List<ContractDynamicTablesFillDTO> dynamicTablesFillDTOList = new ArrayList<>();
        for (TemplateDynamicTablesDO dynamicTablesDO : templateDynamicTablesList) {
            if (StringUtils.isNotBlank(dynamicTablesDO.getKeyword())) {
                float[] keyWordsByPath = PdfUtils.getKeyWordsByPath(contractTemplate.getTemplateUrl(), dynamicTablesDO.getKeyword());
                if (null == keyWordsByPath || keyWordsByPath.length == 0) {
                    continue;
                }
            }
            ContractDynamicTablesFillDTO dynamicTablesFillDTO = TemplateDynamicTablesConvert.INSTANCE.convertDO2FillDTO(dynamicTablesDO);
            if (StringUtils.isNotBlank(dynamicTablesDO.getTableType()) && StringUtils.equals(dynamicTablesDO.getTableType(), ApiConstants.ROOM_ASSET)) {
                // 查询房源房间资产信息
                CommonResult<List<RoomEquipmentVO>> roomEquipmentByRoomId = roomEquipmentApi.getRoomEquipmentByRoomId(roomId);
                List<RoomEquipmentVO> roomAssetsList = new ArrayList<>();
                if (Objects.nonNull(roomEquipmentByRoomId) && CollectionUtils.isNotEmpty(roomEquipmentByRoomId.getData())) {
                    roomAssetsList = roomEquipmentByRoomId.getData();
                }

                List<String[]> dataFillDTOList = new ArrayList<>();
                for (int i = 0; i < roomAssetsList.size(); i++) {
                    RoomEquipmentVO roomEquipmentVO = roomAssetsList.get(i);
                    String[] data = new String[5];
                    data[0] = String.valueOf(i + 1);
                    data[1] = roomEquipmentVO.getName();
                    data[2] = ONE.toString();
                    data[3] = Objects.nonNull(roomEquipmentVO.getPrice()) ? roomEquipmentVO.getPrice().toString() : EMPTY;
                    data[4] = roomEquipmentVO.getRemark();
                    dataFillDTOList.add(data);
                }
                dynamicTablesFillDTO.setDatas(dataFillDTOList);
            }
            dynamicTablesFillDTOList.add(dynamicTablesFillDTO);
        }

        return JSONArray.toJSONString(dynamicTablesFillDTOList);
    }

    @Override
    public ElectronicContractGenerateRespVO autoSign(Long contractId) {
        //查询合同信息
        ContractRoomDO contractDO = getRoomContract(contractId);
        if (!ContractStatusEnum.NO_SIGNED.getCode().equals(contractDO.getContractStatus())) {
            throw exception(CONTRACT_STATUS_UNABLE_LAUNCH_SIGN);
        }
        //查询电子合同记录
        ElectronicContractDO electronicContractDO = getElectronicContract(contractId);
        if (Objects.isNull(electronicContractDO)) {
            throw exception(ELECTRONIC_CONTRACT_NOT_EXIST);
        }
        if (electronicContractDO.getStatus() > 0) {
            return ElectronicContractConvert.INSTANCE.convert(electronicContractDO);
        }

        //查询自动签约的主体信息
        SigningSubjectDO subjectDO = getSubjectDTO(contractDO.getApartmentId());

        //生成甲方签约交易号
        String transactionId = generateNumber(LANDLORD_TRANSACTION_PREFIX + contractId + UNDERLINE, LANDLORD_TRANSACTION_REDIS_KEY);

        String contractNum = electronicContractDO.getContractNum();
        String customerId = subjectDO.getSignatureCode();
        FadadaUploadResponse fadadaUploadResponse = fadadaExtSignAuto(transactionId, contractNum, customerId, SIGN_CONTRACT_NAME);
        LambdaUpdateWrapper<ElectronicContractDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(ElectronicContractDO::getLandlordTransactionId, transactionId);
        String downloadUrl = fadadaUploadResponse.getDownload_url();
        if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
            downloadUrl = downloadUrl.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
        }
        updateWrapper.set(ElectronicContractDO::getDownloadUrl, downloadUrl);
        String viewUrl = fadadaUploadResponse.getViewpdf_url();
        if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
            viewUrl = viewUrl.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
        }
        updateWrapper.set(ElectronicContractDO::getViewpdfUrl, viewUrl);
        updateWrapper.set(ElectronicContractDO::getStatus, ElectronicContractStatusEnum.LANDLORD_SIGNED.getCode());
        updateWrapper.eq(ElectronicContractDO::getId, electronicContractDO.getId());
        electronicContractMapper.update(null, updateWrapper);

        // 修改合同状态 记录甲方签约时间
        ContractUpdateStatusVO contractReq = new ContractUpdateStatusVO();
        contractReq.setId(contractDO.getId());
        contractReq.setSignStatus(ContractSignStatusEnum.SIGNED_PARTY_B.getCode());
        contractReq.setLessorSignTime(LocalDateTime.now());
        contractRoomService.update(contractReq);

        return ElectronicContractConvert.INSTANCE.convert(electronicContractDO);
    }

    /**
     * 法大大自动签约
     */
    private FadadaUploadResponse fadadaExtSignAuto(String transactionId, String contractNum, String customerId, String docTitle) {
        ExtSignAutoParams params = new ExtSignAutoParams();
        //平台自定义唯一交易号
        params.setTransactionId(transactionId);
        //此处传入调用上传或填充合同接口成功时定义的合同编号
        params.setContractId(contractNum);
        //此处传入认证成功后成功申请证书的客户编号
        params.setCustomerId(customerId);
        params.setDocTitle(docTitle);
        //0-关键字（默认）1-坐标
        params.setPositionType(LANDLORD_SIGN_FILLING_TYPE);
        params.setSignKeyword(LANDLORD_SIGN_FILLING_KEYWORDS);
        //0：所有关键字签章  1：第一个关键字签章 ；   2：最后一个关键字签章
        params.setKeywordStrategy(LANDLORD_SIGN_FILLING_KEYWORD_STRATEGY);
        params.setClientRole(LANDLORD_SIGN_FILLING_ROLE);
        FadadaUploadResponse fadadaUploadResponse = fadadaContractServiceApi.extsignAuto(params);
        log.info("甲方自动签约，请求法大大请求参数：{}，返回参数：{}", JSONObject.toJSONString(params), JSONObject.toJSONString(fadadaUploadResponse));
        if (Objects.isNull(fadadaUploadResponse) || Objects.isNull(fadadaUploadResponse.getCode())
                || !StringUtils.equals(fadadaUploadResponse.getCode(), FadadaReturnCodeConstants.OPERATION_SUCCESS.getCode().toString())) {
            throw exception(ELECTRONIC_CONTRACT_AUTO_SIGN_FAILED);
        }
        return fadadaUploadResponse;
    }

    @Override
    public String extsign(Long contractId, String phone) {
        //查询电子合同记录
        ElectronicContractDO electronicContractDO = getElectronicContract(contractId);
        if (Objects.isNull(electronicContractDO)) {
            throw exception(ELECTRONIC_CONTRACT_NOT_LAUNCH);
        }
        if (Objects.isNull(electronicContractDO.getStatus()) ||
                electronicContractDO.getStatus().intValue() != ElectronicContractStatusEnum.LANDLORD_SIGNED.getCode().intValue()) {
            throw exception(CONTRACT_STATUS_UNABLE_SIGN);
        }

        // 获取用户实名签章信息
        MemberUserAuthInfoDTO userAuthInfo = getMemberUserAuthInfoDTO(phone);

        // 生成唯一交易号
        String transactionId = generateNumber(CUSTOMER_TRANSACTION_PREFIX + contractId + UNDERLINE, CUSTOMER_TRANSACTION_REDIS_KEY);
        // 调用法大大手动签约
        ExtSignParams params = new ExtSignParams();
        //平台自定义唯一交易号
        params.setTransactionId(transactionId);
        //此处传入调用上传或填充合同接口成功时定义的合同编号
        params.setContractId(electronicContractDO.getContractNum());
        //此处传入认证成功后成功申请证书的客户编号
        params.setCustomerId(userAuthInfo.getSignatureCode());
        params.setMobileSignType(CUSTOMER_MOBILE_SIGN_TYPE);
        params.setSignKeyword(CUSTOMER_SIGN_KEYWORD);
        params.setDocTitle(SIGN_CONTRACT_NAME);
        // 异步回调地址
        params.setNotifyUrl(EXTSIGN_CALLBACK_URL);
        if (StringUtils.isNotBlank(EXTSIGN_SYNC_CALLBACK_URL)) {
            // 同步回调地址
            params.setReturnUrl(EXTSIGN_SYNC_CALLBACK_URL);
            params.setOpenEnvironment("1");
        }
        params.setSignVerifyWay("1");
        String extsign = fadadaExtSign(params);

        //更新电子合同 租客相关信息
        LambdaUpdateWrapper<ElectronicContractDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(ElectronicContractDO::getCustomerTransactionId, transactionId);
        updateWrapper.set(ElectronicContractDO::getCustomerSignature, userAuthInfo.getSignatureCode());
        updateWrapper.eq(ElectronicContractDO::getId, electronicContractDO.getId());
        electronicContractMapper.update(null, updateWrapper);
        return extsign;
    }

    /**
     * 获取用户实名签章信息
     */
    private MemberUserAuthInfoDTO getMemberUserAuthInfoDTO(String phone) {
        CommonResult<MemberUserAuthInfoDTO> memberUserAuthInfo = memberUserApi.getUserAuthInfo(phone);
        log.info("手动签约，查询用户实名信息，请求参数：{}，响应参数：{}", phone, memberUserAuthInfo);

        if (Objects.isNull(memberUserAuthInfo) || Objects.isNull(memberUserAuthInfo.getData())) {
            throw exception(USER_NOT_AUTH);
        }
        MemberUserAuthInfoDTO userAuthInfo = memberUserAuthInfo.getData();
        if (!userAuthInfo.getAuthenticated()) {
            throw exception(USER_NOT_AUTH);
        }
        return userAuthInfo;
    }

    /**
     * 调用法大大手动签约
     */
    private String fadadaExtSign(ExtSignParams params) {
        String extsign = fadadaContractServiceApi.extsign(params);
        log.info("乙方签约，请求法大大获取签约地址，请求参数：{}，返回参数：{}", JSONObject.toJSONString(params), extsign);
        if (StringUtils.isBlank(extsign)) {
            throw exception(MANUAL_SIGN_GET_ADDRESS_FAILED);
        }
        // 替换地址
        // 把https://dada.creative-apartment.com.cn/ 替换成 https://extapi.fadada.com/api2/
        if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
            extsign = extsign.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
        }
        return extsign;
    }


    @Override
    public void signCallback(ContractSignCallbackReqVO reqVO) {
        if (Objects.isNull(reqVO)) {
            throw exception(CONTRACT_SIGN_RESULT_DATA_ERROR);
        }
        //查询电子合同记录
        if (StringUtils.isBlank(reqVO.getTransaction_id())) {
            throw exception(CONTRACT_SIGN_RESULT_DATA_ERROR);
        }
        LambdaQueryWrapper<ElectronicContractDO> electronicContractQueryWrapper = new LambdaQueryWrapper<>();
        electronicContractQueryWrapper.eq(ElectronicContractDO::getCustomerTransactionId, reqVO.getTransaction_id());
        ElectronicContractDO electronicContractDO = electronicContractMapper.selectOne(electronicContractQueryWrapper);
        if (Objects.isNull(electronicContractDO)) {
            throw exception(ELECTRONIC_CONTRACT_NOT_EXIST);
        }

        if (Objects.nonNull(reqVO.getResult_code()) && reqVO.getResult_code().intValue() == CUSTOMER_CALLBACK_CODE.intValue()
                && !Objects.equals(ElectronicContractStatusEnum.BOTH_SIGNED.getCode(), electronicContractDO.getStatus())) {
            //修改电子合同状态
            LambdaUpdateWrapper<ElectronicContractDO> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(ElectronicContractDO::getStatus, ElectronicContractStatusEnum.BOTH_SIGNED.getCode());
            updateWrapper.set(ElectronicContractDO::getCompleteTime, LocalDateTime.now());

            if (StringUtils.isNotBlank(reqVO.getViewpdf_url()) && StringUtils.isNotBlank(SIGN_TARGET_URL)
                    && reqVO.getViewpdf_url().contains(SIGN_TARGET_URL)){
                //替换地址
                updateWrapper.set(ElectronicContractDO::getViewpdfUrl, reqVO.getViewpdf_url().replace(SIGN_TARGET_URL, SIGN_REPLACE_URL));
            }else {
                updateWrapper.set(ElectronicContractDO::getViewpdfUrl, reqVO.getViewpdf_url());
            }
            if (StringUtils.isNotBlank(reqVO.getDownload_url()) && StringUtils.isNotBlank(SIGN_TARGET_URL)
                    && reqVO.getDownload_url().contains(SIGN_TARGET_URL)){
                //替换地址
                updateWrapper.set(ElectronicContractDO::getDownloadUrl, reqVO.getDownload_url().replace(SIGN_TARGET_URL, SIGN_REPLACE_URL));
            }else{
                updateWrapper.set(ElectronicContractDO::getDownloadUrl, reqVO.getDownload_url());
            }

            updateWrapper.eq(ElectronicContractDO::getId, electronicContractDO.getId());
            electronicContractMapper.update(null, updateWrapper);

            if (ContractTemplateTypeEnum.RENT_CONTRACT.getCode().equals(electronicContractDO.getType())) {
                // 修改合同状态
                TenantUtils.execute(electronicContractDO.getTenantId(), () -> {
                    ContractUpdateStatusVO contractReq = new ContractUpdateStatusVO();
                    contractReq.setId(electronicContractDO.getRoomContractId());
                    contractReq.setSignStatus(ContractSignStatusEnum.SIGNED.getCode());
                    contractReq.setContractStatus(ContractStatusEnum.ACTIVE.getCode());
                    contractReq.setChangeEffectiveTime(LocalDateTime.now());
                    contractRoomService.updateStatusAndElectronicNo(contractReq);
                });
            } else if (ContractTemplateTypeEnum.OWNER_HOUSE_CONTRACT.getCode().equals(electronicContractDO.getType())) {
                // 修改合同状态
                TenantUtils.execute(electronicContractDO.getTenantId(), () -> {
                    contractHouseOwnerService.callbackSignSuccess(electronicContractDO.getOwnerHouseContractId());
                });
            }

            // 合同归档
            fadadaContractFiling(electronicContractDO.getContractNum());
        }

    }

    /**
     * 合同归档
     */
    private void fadadaContractFiling(String contractId) {
        // 合同归档
        ContractFillingParams params = new ContractFillingParams();
        //此处传入调用上传或填充合同接口成功时定义的合同编号
        params.setContractId(contractId);
        Boolean aBoolean = fadadaContractServiceApi.contractFiling(params);
        log.info("合同签约完成调用法大大合同归档接口，请求参数：{}，返回参数：{}", JSONObject.toJSONString(params), aBoolean);
    }

    @Override
    public void deleteByContractId(Long contractId) {
        if (Objects.isNull(contractId)) {
            throw exception(CONTRACT_DOES_NOT_EXISTS);
        }
        LambdaQueryWrapper<ElectronicContractDO> deleteWrapper = new LambdaQueryWrapper<>();
        deleteWrapper.eq(ElectronicContractDO::getRoomContractId, contractId);
        electronicContractMapper.delete(deleteWrapper);
    }

    @Override
    public String getSafeProtocolSignAddr(Long memberId) {
        // 查询未签约同住人数据
        List<ContractRoommateDO> roommateList = contractRoommateService.getRequireSignSafeRoommateList(memberId);
        if (CollectionUtils.isEmpty(roommateList)) {
            return null;
        }
        ContractRoommateDO contractRoommateDO = roommateList.get(ZERO);

        // 获取用户实名签章信息
        MemberUserAuthInfoDTO userAuthInfo = getMemberUserAuthInfoDTO(contractRoommateDO.getRoommatePhone());

        String contractId;
        // 已提交过安全协议签署 不需要再次上传合同甲方签约等操作
        if (StringUtils.isBlank(contractRoommateDO.getProtocolContractId())) {
            // 生成自定义合同ID
            contractId = generateNumber(SECURITY_PROTOCOL_PREFIX + memberId + UNDERLINE, SECURITY_PROTOCOL_REDIS_KEY);
            // =========上传安全协议合同到法大大==============
            UploadDocsParams params = new UploadDocsParams();
            params.setContractId(contractId);
            params.setDocTitle(SECURITY_PROTOCOL_NAME);
            params.setDocUrl(SECURITY_PROTOCOL_FILE);
            params.setDocType(DOC_TYPE);
            fadadaContractServiceApi.uploaddocs(params);

            // =================甲方自动签约协议=============
            // 生成甲方签约交易号
            String transactionId = generateNumber(LANDLORD_TRANSACTION_PREFIX + memberId + UNDERLINE + contractRoommateDO.getHouseContractId(), LANDLORD_TRANSACTION_REDIS_KEY);
            FadadaUploadResponse fadadaUploadResponse = fadadaExtSignAuto(transactionId, contractId, LEASER_CODE, SECURITY_PROTOCOL_NAME);
            contractRoommateDO.setProtocolContractId(contractId);
            contractRoommateDO.setProtocolViewUrl(fadadaUploadResponse.getViewpdf_url());
            contractRoommateDO.setProtocolDownloadUrl(fadadaUploadResponse.getDownload_url());
        } else {
            contractId = contractRoommateDO.getProtocolContractId();
        }

        // ==================获取乙方签约地址==============
        // 生成唯一交易号
        String extSignTransactionId = generateNumber(CUSTOMER_TRANSACTION_PREFIX + memberId + UNDERLINE + contractRoommateDO.getHouseContractId(), CUSTOMER_TRANSACTION_REDIS_KEY);
        // 乙方手动签约获取地址
        ExtSignParams extSignParams = new ExtSignParams();
        //平台自定义唯一交易号
        extSignParams.setTransactionId(extSignTransactionId);
        //此处传入调用上传或填充合同接口成功时定义的合同编号
        extSignParams.setContractId(contractId);
        //此处传入认证成功后成功申请证书的客户编号
        extSignParams.setCustomerId(userAuthInfo.getSignatureCode());
        extSignParams.setMobileSignType(CUSTOMER_MOBILE_SIGN_TYPE);
        extSignParams.setSignKeyword(CUSTOMER_SIGN_KEYWORD);
        extSignParams.setDocTitle(SECURITY_PROTOCOL_NAME);
        // 异步回调地址
        extSignParams.setNotifyUrl(SECURITY_PROTOCOL_CALLBACK_URL);
        if (StringUtils.isNotBlank(SECURITY_PROTOCOL_SYNC_CALLBACK_URL)) {
            // 同步回调地址
            extSignParams.setReturnUrl(SECURITY_PROTOCOL_SYNC_CALLBACK_URL);
            extSignParams.setOpenEnvironment("1");
        }
        extSignParams.setSignVerifyWay("1");
        String signUrl = fadadaExtSign(extSignParams);

        contractRoommateDO.setProtocolTransactionNo(extSignTransactionId);
        contractRoommateService.updateRoommateById(contractRoommateDO);
        return signUrl;
    }


    @Override
    public void signSecurityProtocolCallback(ContractSignCallbackReqVO reqVO) {
        if (Objects.isNull(reqVO)) {
            throw exception(CONTRACT_SIGN_RESULT_DATA_ERROR);
        }
        if (Objects.nonNull(reqVO.getResult_code()) && reqVO.getResult_code().intValue() == CUSTOMER_CALLBACK_CODE.intValue()) {
            TenantUtils.executeIgnore(() -> {
                // 签约成功
                // 查询同住人安全协议签署状态
                ContractRoommateDO param = new ContractRoommateDO();
                param.setProtocolTransactionNo(reqVO.getTransaction_id());
                List<ContractRoommateDO> byParam = contractRoommateService.getByParam(param);
                if (CollectionUtils.isEmpty(byParam)) {
                    throw exception(SECURITY_PROTOCOL_CALLBACK_ROOMMATE_NOT_FOUND);
                }
                ContractRoommateDO contractRoommateDO = byParam.get(ZERO);
                contractRoommateDO.setSignSecurityProtocol(true);
                contractRoommateDO.setProtocolViewUrl(reqVO.getViewpdf_url());
                contractRoommateDO.setProtocolDownloadUrl(reqVO.getDownload_url());
                contractRoommateDO.setStatus(RoommateStatusEnum.CHECKED_ID.getCode().toString());
                // 成功入住
                contractRoommateService.checkInSuccess(contractRoommateDO);
                // 合同归档
                fadadaContractFiling(contractRoommateDO.getProtocolContractId());
            });
        }
    }


    @Override
    public String getDownloadUrl(Long contractId) {
        if (Objects.isNull(contractId)) {
            return null;
        }
        ElectronicContractDO electronicContract = getElectronicContract(contractId);
        if (Objects.isNull(electronicContract) || StringUtils.isBlank(electronicContract.getDownloadUrl())) {
            return null;
        }
        return electronicContract.getDownloadUrl();
    }

    private ContractHouseOwnerRespVO getOwnerContract(Long contractId) {
        ContractHouseOwnerRespVO houseOwnerResp = contractHouseOwnerService.getHouseOwnerResp(contractId);
        if (Objects.isNull(houseOwnerResp)) {
            throw exception(CONTRACT_DOES_NOT_EXISTS);
        }
        return houseOwnerResp;
    }

    private OwnerContractInfoFillDTO getOwnerContractInfoFill(ContractHouseOwnerRespVO ownerContract) {
        OwnerContractInfoFillDTO ownerContractInfoFill = ContractHouseOwnerConvert.INSTANCE.convertRespVo2FillDto(ownerContract);
        // 承租方证件类型
        ownerContractInfoFill.setIdType(IdTypeEnum.getName(ownerContractInfoFill.getIdType()));
        //租期
        ownerContractInfoFill.setStartYear(String.valueOf(ownerContract.getStartTime().getYear()));
        ownerContractInfoFill.setStartMonth(String.valueOf(ownerContract.getStartTime().getMonthValue()));
        ownerContractInfoFill.setStartDay(String.valueOf(ownerContract.getStartTime().getDayOfMonth()));
        ownerContractInfoFill.setEndYear(String.valueOf(ownerContract.getEndTime().getYear()));
        ownerContractInfoFill.setEndMonth(String.valueOf(ownerContract.getEndTime().getMonthValue()));
        ownerContractInfoFill.setEndDay(String.valueOf(ownerContract.getEndTime().getDayOfMonth()));
        ownerContractInfoFill.setSignYear(String.valueOf(ownerContract.getSignedTime().getYear()));
        ownerContractInfoFill.setSignMonth(String.valueOf(ownerContract.getSignedTime().getMonthValue()));
        ownerContractInfoFill.setSignDay(String.valueOf(ownerContract.getSignedTime().getDayOfMonth()));
        ownerContractInfoFill.setStartTimeStr(LocalDateTimeUtil.format(ownerContract.getStartTime(), DateUtils.FORMAT_DATE));
        ownerContractInfoFill.setEndTimeStr(LocalDateTimeUtil.format(ownerContract.getEndTime(), DateUtils.FORMAT_DATE));
        ownerContractInfoFill.setSignTimeStr(LocalDateTimeUtil.format(ownerContract.getSignedTime(), DateUtils.FORMAT_DATE));
        // 免租期
        List<RentFreePeriodDTO> rentFreePeriod = JSONArray.parseArray(JSON.toJSONString(ownerContract.getRentFreePeriod()), RentFreePeriodDTO.class);
        if (CollectionUtils.isNotEmpty(rentFreePeriod)) {
            // 首期免租期月数
            Integer firstFreeMonthNum = 0;
            // 每年免租期月数
            Integer perFreeMonthNum = 0;
            // 免租期
            StringBuilder freeRentStr = new StringBuilder();
            // 总计免租期月数
            Integer freeTotalMonthNum = 0;
            for (int i = 0; i < rentFreePeriod.size(); i++) {
                RentFreePeriodDTO periodDTO = rentFreePeriod.get(i);
                // 首期免租期月数
                if (1 == periodDTO.getYearNo()) {
                    firstFreeMonthNum = periodDTO.getFreePeriodMonthNum();
                } else if (2 == periodDTO.getYearNo()) {
                    perFreeMonthNum = periodDTO.getFreePeriodMonthNum();
                }
                freeTotalMonthNum += periodDTO.getFreePeriodMonthNum();
                String numberChinese = Convert.numberToChinese(periodDTO.getYearNo(), false);
                // 数字转中文 十位数问题 11 ==》 一十一  标点
                if (periodDTO.getYearNo() / TEN == ONE) {
                    numberChinese = numberChinese.substring(ONE);
                }
                String monthStr = (0 == periodDTO.getFreePeriodMonthNum()) ? "/" : periodDTO.getFreePeriodMonthNum().toString();
                String periodStr = String.format("第%s年%s个月", numberChinese, monthStr);
                String punctuation = "，";
                if (i == (rentFreePeriod.size() - 1)) {
                    punctuation = "。";
                }
                freeRentStr.append(periodStr).append(punctuation);
            }
            ownerContractInfoFill.setFirstFreeMonthNum(firstFreeMonthNum.toString());
            ownerContractInfoFill.setAnnuallyFreeMonthNum(perFreeMonthNum.toString());
            ownerContractInfoFill.setFreeTotalMonthNum(freeTotalMonthNum.toString());
            ownerContractInfoFill.setFreeRentStr(freeRentStr.toString());

            // 代收租起始时间
            LocalDate collectingRentStartTime = null;
            RentOrderQueryDTO param = new RentOrderQueryDTO();
            param.setBusinessId(ownerContract.getId());
            param.setOrderType(OrderTypeEnum.OWNER.getCode());
            List<RentOrderDO> listByParam = ownerOrderService.getListByParam(param);
            if (CollectionUtils.isEmpty(listByParam)) {
                ownerContractInfoFill.setCollectingRentStartYear("/");
                ownerContractInfoFill.setCollectingRentStartMonth("/");
                ownerContractInfoFill.setCollectingRentStartDay("/");
            } else {
                for (RentOrderDO rentOrderDO : listByParam) {
                    if (BigDecimal.ZERO.compareTo(rentOrderDO.getRentAmount()) < 0) {
                        if (Objects.isNull(collectingRentStartTime)) {
                            collectingRentStartTime = rentOrderDO.getStartTime();
                        } else if (!collectingRentStartTime.isBefore(rentOrderDO.getStartTime())) {
                            collectingRentStartTime = rentOrderDO.getStartTime();
                        }
                    }
                }
            }
            if (Objects.isNull(collectingRentStartTime)) {
                ownerContractInfoFill.setCollectingRentStartYear("/");
                ownerContractInfoFill.setCollectingRentStartMonth("/");
                ownerContractInfoFill.setCollectingRentStartDay("/");
            } else {
                ownerContractInfoFill.setCollectingRentStartYear(String.valueOf(collectingRentStartTime.getYear()));
                ownerContractInfoFill.setCollectingRentStartMonth(String.valueOf(collectingRentStartTime.getMonthValue()));
                ownerContractInfoFill.setCollectingRentStartDay(String.valueOf(collectingRentStartTime.getDayOfMonth()));
            }
        }
        return ownerContractInfoFill;
    }

    private RoomInfoOwnerFillDTO getOwnerRoomInfo(Long roomId) {
        RoomInfoFillDTO roomInfo = getRoomInfo(roomId);
        RoomInfoOwnerFillDTO roomInfoOwnerFillDTO = new RoomInfoOwnerFillDTO();
        if (Objects.nonNull(roomInfo)) {
            roomInfoOwnerFillDTO = ElectronicContractConvert.INSTANCE.convertFillDto2OwnerFillDto(roomInfo);
            CommonResult<RoomHouseFilingsResp> filingsByRoomId = roomHouseApi.getFilingsByRoomId(roomId);
            if (Objects.isNull(filingsByRoomId)) {
                throw exception(ROOM_NOT_EXIST);
            }
            RoomHouseFilingsResp data = filingsByRoomId.getData();
            if (Objects.isNull(data)) {
                throw exception(ROOM_NOT_EXIST);
            }
            fillOwnerFill(roomInfoOwnerFillDTO, data);
        }
        return roomInfoOwnerFillDTO;
    }

    private void fillOwnerFill(RoomInfoOwnerFillDTO roomInfoOwnerFillDTO, RoomHouseFilingsResp data) {
        roomInfoOwnerFillDTO.setRoomHouseId(data.getRoomHouseId());
        roomInfoOwnerFillDTO.setSuperRoomHouseId(data.getSuperRoomHouseId());
        roomInfoOwnerFillDTO.setExternalHouseId(data.getExternalHouseId());
        roomInfoOwnerFillDTO.setSuperExternalHouseId(data.getSuperExternalHouseId());
        roomInfoOwnerFillDTO.setOwnerName(data.getOwnerName());
        roomInfoOwnerFillDTO.setOwnerPhone(data.getOwnerPhone());
        roomInfoOwnerFillDTO.setOwnerCardNo(data.getOwnerCardNo());
        roomInfoOwnerFillDTO.setOwnerCardType(data.getOwnerCardType());
        roomInfoOwnerFillDTO.setPropertyAddress(data.getAddress());
        roomInfoOwnerFillDTO.setBedRoomNum(data.getBedRoomNum());
        roomInfoOwnerFillDTO.setLivingRoomNum(data.getLivingRoomNum());
        roomInfoOwnerFillDTO.setDecoration(data.getDecoration());
        roomInfoOwnerFillDTO.setPeopleNum(data.getPeopleNum());
        if (PropertyTypeEnum.CHC.getCode() == Integer.parseInt(data.getPropertyType())) {
            roomInfoOwnerFillDTO.setPropertyType("3");
            roomInfoOwnerFillDTO.setPropertyNo(data.getPropertyNo());
        } else if (PropertyTypeEnum.REOC.getCode() == Integer.parseInt(data.getPropertyType())) {
            roomInfoOwnerFillDTO.setPropertyType("1");
            roomInfoOwnerFillDTO.setPropertyNo(data.getPropertyNo());
        } else {
            roomInfoOwnerFillDTO.setPropertyType("9");
            roomInfoOwnerFillDTO.setHouseSourceProof(data.getPropertyNo());
        }
    }


    private String getOwnerTemplateParamerMap(ContractHouseOwnerRespVO ownerContract, ContractTemplateDO contractTemplate) {
        // 获取房源相关对象
        RoomInfoOwnerFillDTO roomInfoFillDTO = getOwnerRoomInfo(ownerContract.getRefRoom().getRoomId());
        log.info("业主合同模板填充，房源相关对象：{}", roomInfoFillDTO);
        // 获取合同相关对象
        OwnerContractInfoFillDTO contractInfoFillDTO = getOwnerContractInfoFill(ownerContract);
        log.info("业主合同模板填充，合同相关对象：{}", contractInfoFillDTO);

        // 解析模板文件
        String[] templateFieldName = PdfUtils.getFieldName(contractTemplate.getTemplateUrl());
        if (templateFieldName.length == ZERO) {
            return EMPTY;
        }
        // 模板文件中的关键字数据
        List<String> templateKeywords = new ArrayList<>(Arrays.asList(templateFieldName));

        // 模板关键字数据
        List<TemplateKeywardsDO> templateKeywardsList = contractTemplateService.getTemplateKeyWordsByTemplateId(contractTemplate.getId(), contractTemplate.getTemplateType());
        if (CollectionUtils.isEmpty(templateKeywardsList)) {
            return EMPTY;
        }
        //关键字
        JSONObject paramter = new JSONObject();
        //合同相关关键字映射
        Map<String, String> contractMap = new HashMap<>();
        //房源相关关键字映射
        Map<String, String> roomMap = new HashMap<>();
        // 默认值
        Map<String, String> defaultValueMap = new HashMap<>();
        for (TemplateKeywardsDO templateKeywardsDO : templateKeywardsList) {
            String mappingEntity = templateKeywardsDO.getMappingEntity();
            if (StringUtils.isNotBlank(templateKeywardsDO.getDefaultValue()) && templateKeywords.contains(templateKeywardsDO.getKeywords())) {
                defaultValueMap.put(templateKeywardsDO.getKeywords(), templateKeywardsDO.getDefaultValue());
            }
            if (StringUtils.isNotBlank(mappingEntity)) {
                String[] split = mappingEntity.split(SPOT);
                if (split.length == TWO) {
                    String entityType = split[ZERO];
                    if (templateKeywords.contains(templateKeywardsDO.getKeywords())) {
                        if (StringUtils.equals(entityType, OWNER_CONTRACT_FILLING_ENTITY_CONTRACT)) {
                            contractMap.put(split[ONE], templateKeywardsDO.getKeywords());
                        } else if (StringUtils.equals(entityType, CONTRACT_FILLING_ENTITY_ROOM)) {
                            roomMap.put(split[ONE], templateKeywardsDO.getKeywords());
                        }
                    }
                }
            }
        }

        try {
            // 遍历出实体类中全部字段
            Field[] contractFields = OwnerContractInfoFillDTO.class.getDeclaredFields();
            for (Field declaredField : contractFields) {
                // 取出实体类中的属性名称
                String fieldName = declaredField.getName();
                if (contractMap.containsKey(fieldName)) {
                    // 取出实体类中属性信息
                    Field declared = OwnerContractInfoFillDTO.class.getDeclaredField(fieldName);
                    //抑制java语言访问检查，反射访问private访问权限的属性值
                    declared.setAccessible(true);
                    // 获取实体类中属性的值
                    Object value = declared.get(contractInfoFillDTO);
                    String keywordName = contractMap.get(fieldName);
                    //赋值
                    if (Objects.nonNull(value)) {
                        paramter.put(keywordName, value.toString());
                    } else if (!defaultValueMap.isEmpty() && defaultValueMap.containsKey(keywordName)) {
                        paramter.put(keywordName, defaultValueMap.get(keywordName));
                    } else {
                        paramter.put(keywordName, EMPTY);
                    }
                    if (!defaultValueMap.isEmpty() && defaultValueMap.containsKey(keywordName)) {
                        defaultValueMap.remove(keywordName);
                    }
                }
            }

            Field[] roomFields = RoomInfoOwnerFillDTO.class.getDeclaredFields();
            for (Field declaredField : roomFields) {
                String fieldName = declaredField.getName();
                if (roomMap.containsKey(fieldName)) {
                    Field declared = RoomInfoOwnerFillDTO.class.getDeclaredField(fieldName);
                    declared.setAccessible(true);
                    Object value = declared.get(roomInfoFillDTO);
                    String keywordName = roomMap.get(fieldName);
                    //赋值
                    if (Objects.nonNull(value)) {
                        paramter.put(keywordName, value.toString());
                    } else if (!defaultValueMap.isEmpty() && defaultValueMap.containsKey(keywordName)) {
                        paramter.put(keywordName, defaultValueMap.get(keywordName));
                    } else {
                        paramter.put(keywordName, EMPTY);
                    }
                    if (!defaultValueMap.isEmpty() && defaultValueMap.containsKey(keywordName)) {
                        defaultValueMap.remove(keywordName);
                    }
                }
            }
        } catch (Exception e) {
            log.error("合同模板填充，反射填充数据异常：", e);
            throw exception(TEMPLATE_KEYWARDS_ASSIGNMENT_ERROR);
        }

        if (!defaultValueMap.isEmpty()) {
            for (String key : defaultValueMap.keySet()) {
                paramter.put(key, defaultValueMap.get(key));
            }
        }

        log.info("合同模板填充，映射完成的关键字：{}", paramter);
        return paramter.toJSONString();
    }

    @Override
    public ElectronicContractGenerateRespVO generateOwnerElectronicContract(Long contractId) {
        ContractHouseOwnerRespVO ownerContract = getOwnerContract(contractId);
        if (Objects.isNull(ownerContract.getRefRoom()) || Objects.isNull(ownerContract.getRefRoom().getApartmentId())) {
            throw exception(CONTRACT_DATA_ABNORMAL);
        }
        ContractHouseOwnerDO contractHouseOwnerDO = ContractHouseOwnerConvert.INSTANCE.convert(ownerContract);

        ElectronicContractGenerateRespVO respVO;
        //查询电子合同信息
        ElectronicContractDO electronicContractDO = getElectronicContractByOwnerContract(contractId);
        if (Objects.nonNull(electronicContractDO) && StringUtils.isNotBlank(electronicContractDO.getDownloadUrl())
                && StringUtils.isNotBlank(electronicContractDO.getViewpdfUrl())) {
            // 有预览地址下载地址数据  直接返回
            respVO = ElectronicContractConvert.INSTANCE.convert(electronicContractDO);
            return respVO;
        }

        // 查询主体实名信息
        SigningSubjectDO subjectDO = getSubjectDTO(ownerContract.getRefRoom().getApartmentId());
        //查询合同模板
        ContractTemplateDO contractTemplate = getContractTemplate(ownerContract.getRefRoom().getApartmentId(), null, ContractTemplateTypeEnum.OWNER_HOUSE_CONTRACT.getCode());

        //生成电子合同编号
        String contractNum = generateNumber(EL_CONTRACT_PREFIX + contractId + UNDERLINE, EL_CONTRACT_REDIS_PREFIX);

        contractHouseOwnerDO.setElectronicNo(contractNum);

        // 需要填充的模板数据
        String parameterMap = getOwnerTemplateParamerMap(ownerContract, contractTemplate);
        // 需要填充的动态表格数据
        String dynamicTables = getOwnerTemplateDynamicTables(contractTemplate, ownerContract.getId());

        // 法大大填充数据返回
        FadadaUploadResponse fadadaUploadResponse = sendFillContract(contractTemplate.getTemplateNo(), contractNum, parameterMap, dynamicTables);
//        FadadaUploadResponse fadadaUploadResponse = sendFillContract(contractTemplate.getTemplateNo(), contractNum, parameterMap, null);
        if (Objects.nonNull(fadadaUploadResponse) && Objects.nonNull(fadadaUploadResponse.getCode())
                && StringUtils.equals(fadadaUploadResponse.getCode(), FadadaReturnCodeConstants.OPERATION_SUCCESS.getCode().toString())) {
            if (Objects.isNull(electronicContractDO)) {
                //新增电子合同
                electronicContractDO = new ElectronicContractDO();
                electronicContractDO.setOwnerHouseContractId(ownerContract.getId());
                electronicContractDO.setLandlordSignature(subjectDO.getSignatureCode());
                electronicContractDO.setStatus(ZERO);
                electronicContractDO.setProvide(ONE);
                String viewUrl = fadadaUploadResponse.getViewpdf_url();
                if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
                    viewUrl = viewUrl.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
                }
                electronicContractDO.setViewpdfUrl(viewUrl);
                String downloadUrl = fadadaUploadResponse.getDownload_url();
                if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
                    downloadUrl = downloadUrl.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
                }
                electronicContractDO.setDownloadUrl(downloadUrl);
                electronicContractDO.setContractNum(contractNum);
                electronicContractDO.setType(ContractTemplateTypeEnum.OWNER_HOUSE_CONTRACT.getCode());
                electronicContractMapper.insert(electronicContractDO);
            } else {
                // 编辑电子合同
                electronicContractDO.setOwnerHouseContractId(ownerContract.getId());
                electronicContractDO.setLandlordSignature(subjectDO.getSignatureCode());
                electronicContractDO.setStatus(ZERO);
                electronicContractDO.setProvide(ONE);
                String viewUrl = fadadaUploadResponse.getViewpdf_url();
                if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
                    viewUrl = viewUrl.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
                }
                electronicContractDO.setViewpdfUrl(viewUrl);
                String downloadUrl = fadadaUploadResponse.getDownload_url();
                if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
                    downloadUrl = downloadUrl.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
                }
                electronicContractDO.setDownloadUrl(downloadUrl);
                electronicContractDO.setContractNum(contractNum);
                electronicContractDO.setType(ContractTemplateTypeEnum.OWNER_HOUSE_CONTRACT.getCode());
                electronicContractMapper.updateById(electronicContractDO);
            }
            // 更新业主合同信息
            contractHouseOwnerService.updateOwner(contractHouseOwnerDO);
            respVO = ElectronicContractConvert.INSTANCE.convert(electronicContractDO);
            return respVO;
        } else {
            throw exception(CONTRACT_TEMPLATE_FILLING_FAILED, fadadaUploadResponse.getMsg());
        }
    }

    private String getOwnerTemplateDynamicTables(ContractTemplateDO contractTemplate, Long ownerContractId) {
        //查询模板动态表格数据
        List<TemplateDynamicTablesDO> templateDynamicTablesList = contractTemplateService.getTemplateDynamicTablesByTemplateId(contractTemplate.getId(), contractTemplate.getTemplateType());
        if (CollectionUtils.isEmpty(templateDynamicTablesList)) {
            return "";
        }

        List<ContractDynamicTablesFillDTO> dynamicTablesFillDTOList = new ArrayList<>();
        for (TemplateDynamicTablesDO dynamicTablesDO : templateDynamicTablesList) {
            ContractDynamicTablesFillDTO dynamicTablesFillDTO = TemplateDynamicTablesConvert.INSTANCE.convertDO2FillDTO(dynamicTablesDO);
            if (StringUtils.isNotBlank(dynamicTablesDO.getTableType()) && StringUtils.equals(dynamicTablesDO.getTableType(), OWNER_FURNITURE)) {
                // 家具家电
                LambdaQueryWrapper<ContractHouseOwnerPropertyDO> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(ContractHouseOwnerPropertyDO::getHouseOwnerId, ownerContractId);
                queryWrapper.eq(ContractHouseOwnerPropertyDO::getType, 1);
                List<ContractHouseOwnerPropertyDO> furnitureList = ownerPropertyMapper.selectList(queryWrapper);

                List<String[]> dataFillDTOList = new ArrayList<>();
                if (CollectionUtils.isNotEmpty(furnitureList)){
                    for (int i = 0; i < furnitureList.size(); i++) {
                        ContractHouseOwnerPropertyDO furniture = furnitureList.get(i);
                        String[] data = new String[6];
                        // 物品
                        data[0] = furniture.getThingName();
                        // 品牌
                        data[1] = furniture.getBrandName();
                        // 数量
                        data[2] = Objects.nonNull(furniture.getNum())?furniture.getNum().toString():"";
                        // 是否包含遥控器
                        data[3] = furniture.getRemoteControl();
                        // 生产年份
                        data[4] = furniture.getProductionYear();
                        // 备注
                        data[5] = furniture.getFurnituredRemarks();
                        dataFillDTOList.add(data);
                    }
                }
                dynamicTablesFillDTO.setDatas(dataFillDTOList);
            }else if (StringUtils.isNotBlank(dynamicTablesDO.getTableType()) && StringUtils.equals(dynamicTablesDO.getTableType(), OWNER_PROPERTY)){
                // 物品凭证
                LambdaQueryWrapper<ContractHouseOwnerPropertyDO> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(ContractHouseOwnerPropertyDO::getHouseOwnerId, ownerContractId);
                queryWrapper.eq(ContractHouseOwnerPropertyDO::getType, 0);
                List<ContractHouseOwnerPropertyDO> furnitureList = ownerPropertyMapper.selectList(queryWrapper);

                List<String[]> dataFillDTOList = new ArrayList<>();
                if (CollectionUtils.isNotEmpty(furnitureList)){
                    for (int i = 0; i < furnitureList.size(); i++) {
                        ContractHouseOwnerPropertyDO furniture = furnitureList.get(i);
                        String[] data = new String[4];
                        // 项目
                        data[0] = furniture.getProjectName();
                        // 是否交付
                        data[1] = furniture.getDeliverFlag();
                        // 交付数量
                        data[2] = Objects.nonNull(furniture.getDeliverNum())?furniture.getDeliverNum().toString():"";
                        // 备注
                        data[3] = furniture.getVoucherRemarks();
                        dataFillDTOList.add(data);
                    }
                }
                dynamicTablesFillDTO.setDatas(dataFillDTOList);
            }
            dynamicTablesFillDTOList.add(dynamicTablesFillDTO);
        }

        return JSONArray.toJSONString(dynamicTablesFillDTOList);
    }

    private ElectronicContractDO getElectronicContractByOwnerContract(Long contractId) {
        //查询电子合同记录
        LambdaQueryWrapper<ElectronicContractDO> electronicContractQueryWrapper = new LambdaQueryWrapper<>();
        electronicContractQueryWrapper.eq(ElectronicContractDO::getOwnerHouseContractId, contractId);
        electronicContractQueryWrapper.orderByDesc(ElectronicContractDO::getId);
        List<ElectronicContractDO> electronicContractDOS = electronicContractMapper.selectList(electronicContractQueryWrapper);
        if (CollectionUtils.isNotEmpty(electronicContractDOS)) {
            return electronicContractDOS.get(0);
        }
        return null;
    }

    @Override
    public ElectronicContractGenerateRespVO ownerAutoSign(Long contractId) {
        //查询合同信息
        ContractHouseOwnerRespVO ownerContract = getOwnerContract(contractId);
        if (!ContractStatusEnum.NO_SIGNED.getCode().equals(ownerContract.getContractStatus())) {
            throw exception(CONTRACT_STATUS_UNABLE_LAUNCH_SIGN);
        }
        //查询电子合同记录
        ElectronicContractDO electronicContractDO = getElectronicContractByOwnerContract(contractId);
        if (Objects.isNull(electronicContractDO)) {
            throw exception(ELECTRONIC_CONTRACT_NOT_EXIST);
        }
        if (electronicContractDO.getStatus() > 0) {
            return ElectronicContractConvert.INSTANCE.convert(electronicContractDO);
        }

        //查询自动签约的主体信息
        SigningSubjectDO subjectDO = getSubjectDTO(ownerContract.getRefRoom().getApartmentId());

        //生成甲方签约交易号
        String transactionId = generateNumber(OWNER_LANDLORD_TRANSACTION_PREFIX + contractId + UNDERLINE, LANDLORD_TRANSACTION_REDIS_KEY);

        String contractNum = electronicContractDO.getContractNum();
        String customerId = subjectDO.getSignatureCode();
        FadadaUploadResponse fadadaUploadResponse = fadadaExtSignAuto(transactionId, contractNum, customerId, SIGN_CONTRACT_NAME);
        LambdaUpdateWrapper<ElectronicContractDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(ElectronicContractDO::getLandlordTransactionId, transactionId);
        String downloadUrl = fadadaUploadResponse.getViewpdf_url();
        if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
            downloadUrl = downloadUrl.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
        }
        updateWrapper.set(ElectronicContractDO::getDownloadUrl, downloadUrl);
        String viewUrl = fadadaUploadResponse.getViewpdf_url();
        if (StringUtils.isNotBlank(SIGN_TARGET_URL) && StringUtils.isNotBlank(SIGN_REPLACE_URL)){
            viewUrl = viewUrl.replace(SIGN_TARGET_URL, SIGN_REPLACE_URL);
        }
        updateWrapper.set(ElectronicContractDO::getViewpdfUrl, viewUrl);
        updateWrapper.set(ElectronicContractDO::getStatus, ElectronicContractStatusEnum.LANDLORD_SIGNED.getCode());
        updateWrapper.eq(ElectronicContractDO::getId, electronicContractDO.getId());
        electronicContractMapper.update(null, updateWrapper);

        // 修改合同状态 记录甲方签约时间
        // 更新业主合同信息
        ContractHouseOwnerDO contractHouseOwnerDO = ContractHouseOwnerConvert.INSTANCE.convert(ownerContract);
        contractHouseOwnerDO.setSignStatus(ContractSignStatusEnum.SIGNED_PARTY_B.getCode());
        contractHouseOwnerDO.setLessorSignTime(LocalDateTime.now());
        contractHouseOwnerService.updateOwner(contractHouseOwnerDO);

        return ElectronicContractConvert.INSTANCE.convert(electronicContractDO);
    }

    @Override
    public String ownerExtsign(Long contractId, String phone) {
        //查询电子合同记录
        ElectronicContractDO electronicContractDO = getElectronicContractByOwnerContract(contractId);
        if (Objects.isNull(electronicContractDO)) {
            throw exception(ELECTRONIC_CONTRACT_NOT_LAUNCH);
        }
        if (Objects.isNull(electronicContractDO.getStatus()) ||
                electronicContractDO.getStatus().intValue() != ElectronicContractStatusEnum.LANDLORD_SIGNED.getCode().intValue()) {
            throw exception(CONTRACT_STATUS_UNABLE_SIGN);
        }

        //查询合同信息
        ContractHouseOwnerRespVO ownerContract = getOwnerContract(contractId);
        if (!ContractStatusEnum.NO_SIGNED.getCode().equals(ownerContract.getContractStatus())) {
            throw exception(CONTRACT_STATUS_UNABLE_SIGN);
        }

        // 获取用户实名签章信息
        MemberUserAuthInfoDTO userAuthInfo = getMemberUserAuthInfoDTO(phone);

        // 生成唯一交易号
        String transactionId = generateNumber(OWNER_CUSTOMER_TRANSACTION_PREFIX + contractId + UNDERLINE, CUSTOMER_TRANSACTION_REDIS_KEY);
        // 调用法大大手动签约
        ExtSignParams params = new ExtSignParams();
        //平台自定义唯一交易号
        params.setTransactionId(transactionId);
        //此处传入调用上传或填充合同接口成功时定义的合同编号
        params.setContractId(electronicContractDO.getContractNum());
        //此处传入认证成功后成功申请证书的客户编号
        params.setCustomerId(userAuthInfo.getSignatureCode());
        params.setMobileSignType(CUSTOMER_MOBILE_SIGN_TYPE);
        params.setSignKeyword(CUSTOMER_SIGN_KEYWORD);
        params.setDocTitle(SIGN_CONTRACT_NAME);
        // 异步回调地址
        params.setNotifyUrl(EXTSIGN_CALLBACK_URL);
        if (StringUtils.isNotBlank(OWNER_EXTSIGN_SYNC_CALLBACK_URL)) {
            // 同步回调地址
            params.setReturnUrl(OWNER_EXTSIGN_SYNC_CALLBACK_URL);
            params.setOpenEnvironment("1");
        }
        params.setSignVerifyWay("1");
        String extsign = fadadaExtSign(params);

        //更新电子合同 租客相关信息
        LambdaUpdateWrapper<ElectronicContractDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(ElectronicContractDO::getCustomerTransactionId, transactionId);
        updateWrapper.set(ElectronicContractDO::getCustomerSignature, userAuthInfo.getSignatureCode());
        updateWrapper.eq(ElectronicContractDO::getId, electronicContractDO.getId());
        electronicContractMapper.update(null, updateWrapper);
        return extsign;
    }

    @Override
    public void tabletSignCallback(ContractTabletSignCallbackReqVO reqVO) {
        if (Objects.isNull(reqVO)) {
            throw exception(CONTRACT_TABLET_SIGN_RESULT_DATA_ERROR);
        }
        if (Objects.isNull(reqVO.getContractId())) {
            throw exception(CONTRACT_TABLET_SIGN_RESULT_DATA_ERROR);
        }
        LambdaQueryWrapper<ElectronicContractDO> wrapper = new LambdaQueryWrapper<ElectronicContractDO>()
                .eq(ElectronicContractDO::getRoomContractId, reqVO.getContractId());
        ElectronicContractDO electronicContractDO = electronicContractMapper.selectOne(wrapper);
        if (Objects.isNull(electronicContractDO)) {
            throw exception(ELECTRONIC_CONTRACT_NOT_EXIST);
        }
        if (Objects.nonNull(reqVO.getResultCode()) && CUSTOMER_CALLBACK_CODE.intValue() == reqVO.getResultCode()) {
            //修改电子合同状态
            LambdaUpdateWrapper<ElectronicContractDO> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(ElectronicContractDO::getStatus, ElectronicContractStatusEnum.BOTH_SIGNED.getCode());
            updateWrapper.set(ElectronicContractDO::getCompleteTime, LocalDateTime.now());
            updateWrapper.set(ElectronicContractDO::getViewpdfUrl, reqVO.getViewpdfUrl());
            updateWrapper.set(ElectronicContractDO::getDownloadUrl, reqVO.getDownloadUrl());
            updateWrapper.eq(ElectronicContractDO::getId, electronicContractDO.getId());
            electronicContractMapper.update(null, updateWrapper);
                // 修改合同状态
                TenantUtils.execute(electronicContractDO.getTenantId(), () -> {
                    ContractUpdateStatusVO contractReq = new ContractUpdateStatusVO();
                    contractReq.setId(electronicContractDO.getRoomContractId());
                    contractReq.setSignStatus(ContractSignStatusEnum.SIGNED.getCode());
                    contractReq.setContractStatus(ContractStatusEnum.ACTIVE.getCode());
                    contractReq.setChangeEffectiveTime(LocalDateTime.now());
                    contractRoomService.updateStatusAndElectronicNo(contractReq);
                });
        }

        // 合同归档
        fadadaContractFiling(electronicContractDO.getContractNum());
    }

    @Override
    public TabletContractRespVO tabletContractSign(Long contractId) {
        //查询电子合同信息
        ElectronicContractDO electronicContractDO = getElectronicContract(contractId);
        if (Objects.isNull(electronicContractDO)) {
            ErrorCode ELECTRONIC_CONTRACT_NOT_LAUNCH = new ErrorCode(1014002038, "合同尚未发起签约");
            throw exception(ELECTRONIC_CONTRACT_NOT_LAUNCH);
        }
        if (Objects.isNull(electronicContractDO.getStatus()) ||
                electronicContractDO.getStatus().intValue() != ElectronicContractStatusEnum.LANDLORD_SIGNED.getCode().intValue()) {
            throw exception(CONTRACT_STATUS_UNABLE_SIGN);
        }
        TabletContractRespVO tabletContractRespVO = new TabletContractRespVO()
                .setContractId(electronicContractDO.getRoomContractId())
                .setDownloadUrl(electronicContractDO.getDownloadUrl());
        //计算坐标
        try {
            String keyword = "乙方（签字";
            List<MatchItem> items = new ArrayList();
            PdfReader reader = new PdfReader(electronicContractDO.getDownloadUrl());
            int pageSize = reader.getNumberOfPages();
            for (int page = 1; page <= pageSize; page++) {
                items.addAll(PdfKeyWordUtil.matchPage(reader, page, keyword));
            }
            if (items != null && items.size() > 0) {
                tabletContractRespVO.setMatchItems(items);
            }
        } catch (Exception e) {
            log.error("获取签字坐标异常");
        }
        return tabletContractRespVO;
    }
}
